package com.kirkkt.java.arithmetic;

import com.kirkkt.java.arithmetic.Node;

public class Arithmetic {
    public static void main(String[] args) {
        if (args.length > 0) {
            new Arithmetic().runCalculation(args[0]);
        }
    }

    public Arithmetic() {
    }

    public void runCalculation(String input) {
        Node node = buildTree(input);
        System.out.println(preorderPrint(node));
        System.out.println(preorderCalculate(node));
    }

    // @test
    public int getOperatorIndex(String expression) {
        int balance = 0;
        for (int i = 0; i < expression.length(); i++) {
            if (balance == 0 && isOperator(expression.charAt(i))) {
                return i;
            }
            if (expression.charAt(i) == '(') {
                balance++;
            } else if (expression.charAt(i) == ')') {
                balance--;
            }
        }
        return -1;
    }

    // @test
    public String getInnerExpression(String expression) {
        int length = expression.length();
        if (expression.charAt(0) != '(' || expression.charAt(length - 1) != ')') {
            return expression;
        }
        return expression.substring(1, length - 1);
    }

    public Node buildTree(String expression) {
        String inner = getInnerExpression(expression);
        int operatorIndex = getOperatorIndex(inner);

        if (operatorIndex == -1) {
            return new Node(inner);
        }
        Node left = buildTree(inner.substring(0, operatorIndex));
        Node right = buildTree(inner.substring(operatorIndex + 1, inner.length()));
        return new Node("" + inner.charAt(operatorIndex), left, right);
    }

    public String preorderPrint(Node node) {
        if (node == null) {
            return "";
        }
        return "" + node.value + " " + preorderPrint(node.left) + preorderPrint(node.right) + "";
    }

    public String inorderPrint(Node node) {
        if (node == null) {
            return "";
        }
        return inorderPrint(node.left) + node.value + inorderPrint(node.right);
    }

    public double preorderCalculate(Node node) {
        if (isOperator(node.value)) {
            return calculate(node.value, preorderCalculate(node.left), preorderCalculate(node.right));
        } else {
            return Double.parseDouble(node.value);
        }
    }

    public boolean isOperator(char input) {
        if (input == '+') return true;
        if (input == '-') return true;
        if (input == '*') return true;
        if (input == '/') return true;
        return false;
    }

    public boolean isOperator(String input) {
        if (input.length() != 1) return false;
        return isOperator(input.charAt(0));
    }

    // @ test
    public double calculate(String operator, double left, double right) {
        if (!isOperator(operator)) {
            throw new RuntimeException("illegal operator: " + operator);
        }
        switch (operator.charAt(0)) {
            case '+': {
                return left + right;
            }
            case '-': {
                return left - right;
            }
            case '*': {
                return left * right;
            }
            case '/': {
                return left / right;
            }
        }
        throw new RuntimeException("unable to calculate based on operator: " + operator);
    }
}
